﻿using System.Collections.Generic;
using Core;
using NUnit.Framework;

namespace Tests
{
    [TestFixture]
    public class BoardTests
    {
        public Board boardNoCentralTile;
        public Board boardSingleCentralTile;
        public Board boardMultiCentralTile;

        // a
        //  - a
        //    - END_WORD
        //    - l
        //      - i
        //        - i
        //          - END_WORD
        // b
        // c
        // d

        [SetUp]
        public void Setup()
        {
            //                             0   1   2 
            //                               3   4
            //                             5   6   7
            boardNoCentralTile = new Board(3,
                                           Tiles.A, Tiles.B, Tiles.C,
                                             Tiles.D, Tiles.E,
                                           Tiles.F, Tiles.G, Tiles.H);           
            
            //                                 0   1   2   3
            //                                   4   5   6
            //                                 7   8   9  10
            boardSingleCentralTile = new Board(4,
                                               Tiles.A, Tiles.B, Tiles.C, Tiles.D,
                                                 Tiles.E, Tiles.F, Tiles.G,
                                               Tiles.H, Tiles.I, Tiles.J, Tiles.K);

            //                                0   1   2   3   
            //                                  4   5   6
            //                                7   8   9   10  
            //                                  11  12  13
            //                                14  15  16  17  
            //                                  18  19  20
            //                                21  22  23  24  
            boardMultiCentralTile = new Board(4,
                                              Tiles.A, Tiles.B, Tiles.C, Tiles.D,
                                                Tiles.E, Tiles.F, Tiles.G,
                                              Tiles.H, Tiles.I, Tiles.J, Tiles.K,
                                                Tiles.L, Tiles.M, Tiles.N,
                                              Tiles.O, Tiles.P, Tiles.Q, Tiles.R,
                                                Tiles.S, Tiles.T, Tiles.U,
                                              Tiles.V, Tiles.W, Tiles.X, Tiles.Y); 
        }

        [Test]
        public void Should_create_board_with_correct_number_of_tiles()
        {
            Assert.AreEqual(11, boardSingleCentralTile.AllTiles.Count);
        }

        [Test]
        public void Should_create_board_with_correct_tile_index()
        {
            Assert.AreEqual(Tiles.A, boardSingleCentralTile.AllTiles[0]);
        }

        #region No Central Tile (no memory)

        [Test]
        public void Should_generate_correct_list_of_valid_moves_no_central_tile_from_A()
        {
            var moves = boardNoCentralTile.MovesFrom[0];
            Assert.AreEqual(2, moves.Count);

            Assert.AreEqual(1, moves[0]);
            Assert.AreEqual(3, moves[1]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_no_central_tile_from_B()
        {
            var moves = boardNoCentralTile.MovesFrom[1];
            Assert.AreEqual(4, moves.Count);

            Assert.AreEqual(2, moves[0]);
            Assert.AreEqual(4, moves[1]);
            Assert.AreEqual(3, moves[2]);
            Assert.AreEqual(0, moves[3]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_no_central_tile_from_C()
        {
            var moves = boardNoCentralTile.MovesFrom[2];
            Assert.AreEqual(2, moves.Count);

            Assert.AreEqual(4, moves[0]);
            Assert.AreEqual(1, moves[1]);
        }


        [Test]
        public void Should_generate_correct_list_of_valid_moves_no_central_tile_from_D()
        {
            var moves = boardNoCentralTile.MovesFrom[3];
            Assert.AreEqual(5, moves.Count);

            Assert.AreEqual(4, moves[0]);
            Assert.AreEqual(6, moves[1]);
            Assert.AreEqual(5, moves[2]);
            Assert.AreEqual(0, moves[3]);
            Assert.AreEqual(1, moves[4]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_no_central_tile_from_E()
        {
            var moves = boardNoCentralTile.MovesFrom[4];
            Assert.AreEqual(5, moves.Count);

            Assert.AreEqual(7, moves[0]);
            Assert.AreEqual(6, moves[1]);
            Assert.AreEqual(3, moves[2]);
            Assert.AreEqual(1, moves[3]);
            Assert.AreEqual(2, moves[4]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_no_central_tile_from_F()
        {
            var moves = boardNoCentralTile.MovesFrom[5];
            Assert.AreEqual(2, moves.Count);

            Assert.AreEqual(6, moves[0]);
            Assert.AreEqual(3, moves[1]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_no_central_tile_from_G()
        {
            var moves = boardNoCentralTile.MovesFrom[6];
            Assert.AreEqual(4, moves.Count);

            Assert.AreEqual(7, moves[0]);
            Assert.AreEqual(5, moves[1]);
            Assert.AreEqual(3, moves[2]);
            Assert.AreEqual(4, moves[3]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_no_central_tile_from_H()
        {
            var moves = boardNoCentralTile.MovesFrom[7];
            Assert.AreEqual(2, moves.Count);

            Assert.AreEqual(6, moves[0]);
            Assert.AreEqual(4, moves[1]);
        }

        #endregion

        #region Single Central Tile (no-memory)

        [Test]
        public void Should_generate_correct_list_of_valid_moves_single_central_tile_from_A()
        {
            var moves = boardSingleCentralTile.MovesFrom[0];
            Assert.AreEqual(2, moves.Count);

            Assert.AreEqual(1, moves[0]);
            Assert.AreEqual(4, moves[1]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_single_central_tile_from_B()
        {
            var moves = boardSingleCentralTile.MovesFrom[1];
            Assert.AreEqual(4, moves.Count);

            Assert.AreEqual(2, moves[0]);
            Assert.AreEqual(5, moves[1]);
            Assert.AreEqual(4, moves[2]);
            Assert.AreEqual(0, moves[3]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_single_central_tile_from_C()
        {
            var moves = boardSingleCentralTile.MovesFrom[2];
            Assert.AreEqual(4, moves.Count);

            Assert.AreEqual(3, moves[0]);
            Assert.AreEqual(6, moves[1]);
            Assert.AreEqual(5, moves[2]);
            Assert.AreEqual(1, moves[3]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_single_central_tile_from_D()
        {
            var moves = boardSingleCentralTile.MovesFrom[3];
            Assert.AreEqual(2, moves.Count);

            Assert.AreEqual(6, moves[0]);
            Assert.AreEqual(2, moves[1]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_single_central_tile_from_E()
        {
            var moves = boardSingleCentralTile.MovesFrom[4];
            Assert.AreEqual(5, moves.Count);

            Assert.AreEqual(5, moves[0]);
            Assert.AreEqual(8, moves[1]);
            Assert.AreEqual(7, moves[2]);
            Assert.AreEqual(0, moves[3]);
            Assert.AreEqual(1, moves[4]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_single_central_tile_from_F()
        {
            var moves = boardSingleCentralTile.MovesFrom[5];
            Assert.AreEqual(6, moves.Count);

            Assert.AreEqual(6, moves[0]);
            Assert.AreEqual(9, moves[1]);
            Assert.AreEqual(8, moves[2]);
            Assert.AreEqual(4, moves[3]);
            Assert.AreEqual(1, moves[4]);
            Assert.AreEqual(2, moves[5]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_single_central_tile_from_G()
        {
            var moves = boardSingleCentralTile.MovesFrom[6];
            Assert.AreEqual(5, moves.Count);

            Assert.AreEqual(10, moves[0]);
            Assert.AreEqual(9, moves[1]);
            Assert.AreEqual(5, moves[2]);
            Assert.AreEqual(2, moves[3]);
            Assert.AreEqual(3, moves[4]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_single_central_tile_from_H()
        {
            var moves = boardSingleCentralTile.MovesFrom[7];
            Assert.AreEqual(2, moves.Count);

            Assert.AreEqual(8, moves[0]);
            Assert.AreEqual(4, moves[1]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_single_central_tile_from_I()
        {
            var moves = boardSingleCentralTile.MovesFrom[8];
            Assert.AreEqual(4, moves.Count);

            Assert.AreEqual(9, moves[0]);
            Assert.AreEqual(7, moves[1]);
            Assert.AreEqual(4, moves[2]);
            Assert.AreEqual(5, moves[3]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_single_central_tile_from_J()
        {
            var moves = boardSingleCentralTile.MovesFrom[9];
            Assert.AreEqual(4, moves.Count);

            Assert.AreEqual(10, moves[0]);
            Assert.AreEqual(8, moves[1]);
            Assert.AreEqual(5, moves[2]);
            Assert.AreEqual(6, moves[3]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_single_central_tile_from_K()
        {
            var moves = boardSingleCentralTile.MovesFrom[10];
            Assert.AreEqual(2, moves.Count);

            Assert.AreEqual(9, moves[0]);
            Assert.AreEqual(6, moves[1]);
        }


        #endregion

        #region Multi Central Tile (no-memory)

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_A()
        {
            var moves = boardMultiCentralTile.MovesFrom[0];
            Assert.AreEqual(2, moves.Count);

            Assert.AreEqual(1, moves[0]);
            Assert.AreEqual(4, moves[1]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_B()
        {
            var moves = boardMultiCentralTile.MovesFrom[1];
            Assert.AreEqual(4, moves.Count);

            Assert.AreEqual(2, moves[0]);
            Assert.AreEqual(5, moves[1]);
            Assert.AreEqual(4, moves[2]);
            Assert.AreEqual(0, moves[3]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_C()
        {
            var moves = boardMultiCentralTile.MovesFrom[2];
            Assert.AreEqual(4, moves.Count);

            Assert.AreEqual(3, moves[0]);
            Assert.AreEqual(6, moves[1]);
            Assert.AreEqual(5, moves[2]);
            Assert.AreEqual(1, moves[3]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_D()
        {
            var moves = boardMultiCentralTile.MovesFrom[3];
            Assert.AreEqual(2, moves.Count);

            Assert.AreEqual(6, moves[0]);
            Assert.AreEqual(2, moves[1]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_E()
        {
            var moves = boardMultiCentralTile.MovesFrom[4];
            Assert.AreEqual(5, moves.Count);

            Assert.AreEqual(5, moves[0]);
            Assert.AreEqual(8, moves[1]);
            Assert.AreEqual(7, moves[2]);
            Assert.AreEqual(0, moves[3]);
            Assert.AreEqual(1, moves[4]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_F()
        {
            var moves = boardMultiCentralTile.MovesFrom[5];
            Assert.AreEqual(6, moves.Count);

            Assert.AreEqual(6, moves[0]);
            Assert.AreEqual(9, moves[1]);
            Assert.AreEqual(8, moves[2]);
            Assert.AreEqual(4, moves[3]);
            Assert.AreEqual(1, moves[4]);
            Assert.AreEqual(2, moves[5]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_G()
        {
            var moves = boardMultiCentralTile.MovesFrom[6];
            Assert.AreEqual(5, moves.Count);

            Assert.AreEqual(10, moves[0]);
            Assert.AreEqual(9, moves[1]);
            Assert.AreEqual(5, moves[2]);
            Assert.AreEqual(2, moves[3]);
            Assert.AreEqual(3, moves[4]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_H()
        {
            var moves = boardMultiCentralTile.MovesFrom[7];
            Assert.AreEqual(3, moves.Count);

            Assert.AreEqual(8, moves[0]);
            Assert.AreEqual(11, moves[1]);
            Assert.AreEqual(4, moves[2]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_I()
        {
            var moves = boardMultiCentralTile.MovesFrom[8];
            Assert.AreEqual(6, moves.Count);

            Assert.AreEqual(9, moves[0]);
            Assert.AreEqual(12, moves[1]);
            Assert.AreEqual(11, moves[2]);
            Assert.AreEqual(7, moves[3]);
            Assert.AreEqual(4, moves[4]);
            Assert.AreEqual(5, moves[5]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_J()
        {
            var moves = boardMultiCentralTile.MovesFrom[9];
            Assert.AreEqual(6, moves.Count);

            Assert.AreEqual(10, moves[0]);
            Assert.AreEqual(13, moves[1]);
            Assert.AreEqual(12, moves[2]);
            Assert.AreEqual(8, moves[3]);
            Assert.AreEqual(5, moves[4]);
            Assert.AreEqual(6, moves[5]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_K()
        {
            var moves = boardMultiCentralTile.MovesFrom[10];
            Assert.AreEqual(3, moves.Count);

            Assert.AreEqual(13, moves[0]);
            Assert.AreEqual(9, moves[1]);
            Assert.AreEqual(6, moves[2]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_L()
        {
            var moves = boardMultiCentralTile.MovesFrom[11];
            Assert.AreEqual(5, moves.Count);

            Assert.AreEqual(12, moves[0]);
            Assert.AreEqual(15, moves[1]);
            Assert.AreEqual(14, moves[2]);
            Assert.AreEqual(7, moves[3]);
            Assert.AreEqual(8, moves[4]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_M()
        {
            var moves = boardMultiCentralTile.MovesFrom[12];
            Assert.AreEqual(6, moves.Count);

            Assert.AreEqual(13, moves[0]);
            Assert.AreEqual(16, moves[1]);
            Assert.AreEqual(15, moves[2]);
            Assert.AreEqual(11, moves[3]);
            Assert.AreEqual(8, moves[4]);
            Assert.AreEqual(9, moves[5]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_N()
        {
            var moves = boardMultiCentralTile.MovesFrom[13];
            Assert.AreEqual(5, moves.Count);

            Assert.AreEqual(17, moves[0]);
            Assert.AreEqual(16, moves[1]);
            Assert.AreEqual(12, moves[2]);
            Assert.AreEqual(9, moves[3]);
            Assert.AreEqual(10, moves[4]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_O()
        {
            var moves = boardMultiCentralTile.MovesFrom[14];
            Assert.AreEqual(3, moves.Count);

            Assert.AreEqual(15, moves[0]);
            Assert.AreEqual(18, moves[1]);
            Assert.AreEqual(11, moves[2]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_P()
        {
            var moves = boardMultiCentralTile.MovesFrom[15];
            Assert.AreEqual(6, moves.Count);

            Assert.AreEqual(16, moves[0]);
            Assert.AreEqual(19, moves[1]);
            Assert.AreEqual(18, moves[2]);
            Assert.AreEqual(14, moves[3]);
            Assert.AreEqual(11, moves[4]);
            Assert.AreEqual(12, moves[5]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_Q()
        {
            var moves = boardMultiCentralTile.MovesFrom[16];
            Assert.AreEqual(6, moves.Count);

            Assert.AreEqual(17, moves[0]);
            Assert.AreEqual(20, moves[1]);
            Assert.AreEqual(19, moves[2]);
            Assert.AreEqual(15, moves[3]);
            Assert.AreEqual(12, moves[4]);
            Assert.AreEqual(13, moves[5]);
        }
        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_R()
        {
            var moves = boardMultiCentralTile.MovesFrom[17];
            Assert.AreEqual(3, moves.Count);

            Assert.AreEqual(20, moves[0]);
            Assert.AreEqual(16, moves[1]);
            Assert.AreEqual(13, moves[2]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_S()
        {
            var moves = boardMultiCentralTile.MovesFrom[18];
            Assert.AreEqual(5, moves.Count);

            Assert.AreEqual(19, moves[0]);
            Assert.AreEqual(22, moves[1]);
            Assert.AreEqual(21, moves[2]);
            Assert.AreEqual(14, moves[3]);
            Assert.AreEqual(15, moves[4]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_T()
        {
            var moves = boardMultiCentralTile.MovesFrom[19];
            Assert.AreEqual(6, moves.Count);

            Assert.AreEqual(20, moves[0]);
            Assert.AreEqual(23, moves[1]);
            Assert.AreEqual(22, moves[2]);
            Assert.AreEqual(18, moves[3]);
            Assert.AreEqual(15, moves[4]);
            Assert.AreEqual(16, moves[5]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_U()
        {
            var moves = boardMultiCentralTile.MovesFrom[20];
            Assert.AreEqual(5, moves.Count);

            Assert.AreEqual(24, moves[0]);
            Assert.AreEqual(23, moves[1]);
            Assert.AreEqual(19, moves[2]);
            Assert.AreEqual(16, moves[3]);
            Assert.AreEqual(17, moves[4]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_V()
        {
            var moves = boardMultiCentralTile.MovesFrom[21];
            Assert.AreEqual(2, moves.Count);

            Assert.AreEqual(22, moves[0]);
            Assert.AreEqual(18, moves[1]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_W()
        {
            var moves = boardMultiCentralTile.MovesFrom[22];
            Assert.AreEqual(4, moves.Count);

            Assert.AreEqual(23, moves[0]);
            Assert.AreEqual(21, moves[1]);
            Assert.AreEqual(18, moves[2]);
            Assert.AreEqual(19, moves[3]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_X()
        {
            var moves = boardMultiCentralTile.MovesFrom[23];
            Assert.AreEqual(4, moves.Count);

            Assert.AreEqual(24, moves[0]);
            Assert.AreEqual(22, moves[1]);
            Assert.AreEqual(19, moves[2]);
            Assert.AreEqual(20, moves[3]);
        }

        [Test]
        public void Should_generate_correct_list_of_valid_moves_multi_central_tile_from_Y()
        {
            var moves = boardMultiCentralTile.MovesFrom[24];
            Assert.AreEqual(2, moves.Count);

            Assert.AreEqual(23, moves[0]);
            Assert.AreEqual(20, moves[1]);
        }
        #endregion


        [Test]
        public void Should_have_no_valid_moves_left_if_cornered()
        {
            var history = new List<int> {1, 3};
            var moves = boardNoCentralTile.LeftoverMoves(0, history);

            Assert.AreEqual(0, moves.Count);
        }

        [Test]
        public void Should_have_no_valid_moves_left_if_stranded()
        {
            var history = new List<int> { 13, 16,15,11,8,9 };
            var moves = boardMultiCentralTile.LeftoverMoves(12, history);

            Assert.AreEqual(0, moves.Count);
        }

        [Test]
        public void Should_have_valid_moves_left_with_history()
        {
            var history = new List<int> { 0,1,2 };
            var moves = boardMultiCentralTile.LeftoverMoves(5, history);

            Assert.AreEqual(4, moves.Count);

            Assert.AreEqual(6, moves[0]);
            Assert.AreEqual(9, moves[1]);
            Assert.AreEqual(8, moves[2]);
            Assert.AreEqual(4, moves[3]);
        }

        [Test]
        public void Should_have_valid_moves_left_with_empty_history()
        {
            var history = new List<int>();
            var moves = boardMultiCentralTile.LeftoverMoves(5, history);

            Assert.AreEqual(6, moves.Count);

            Assert.AreEqual(6, moves[0]);
            Assert.AreEqual(9, moves[1]);
            Assert.AreEqual(8, moves[2]);
            Assert.AreEqual(4, moves[3]);
            Assert.AreEqual(1, moves[4]);
            Assert.AreEqual(2, moves[5]);
        }
    }
}

